﻿using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;

using JetBrains.Annotations;

using static CodeJam.Targeting.MethodImplOptionsEx;

namespace CodeJam.IO
{
	/// <summary>IO assertions class.</summary>
	[PublicAPI]
	public static class IoCode
	{
		/// <summary>Asserts that specified path is either absolute or relative not rooted path.</summary>
		/// <param name="path">The path.</param>
		/// <param name="argName">Name of the argument.</param>
		[DebuggerHidden, MethodImpl(AggressiveInlining)]
		[AssertionMethod]
		public static void IsWellFormedPath(
			string path,
			[InvokerParameterName] string argName)
		{
			Code.NotNullNorEmpty(path, argName);
			if (!PathHelper.IsWellFormedPath(path))
				throw IoCodeExceptions.ArgumentNotWellFormedPath(argName, path);
		}

		/// <summary>Asserts that specified path is well-formed full path.</summary>
		/// <param name="path">The path.</param>
		/// <param name="argName">Name of the argument.</param>
		[DebuggerHidden, MethodImpl(AggressiveInlining)]
		[AssertionMethod]
		public static void IsWellFormedAbsolutePath(
			string path,
			[InvokerParameterName] string argName)
		{
			Code.NotNullNorEmpty(path, argName);
			if (!PathHelper.IsWellFormedAbsolutePath(path))
				throw IoCodeExceptions.ArgumentNotWellFormedAbsolutePath(argName, path);
		}

		/// <summary>Asserts that specified path is well-formed relative path.</summary>
		/// <param name="path">The path.</param>
		/// <param name="argName">Name of the argument.</param>
		[DebuggerHidden, MethodImpl(AggressiveInlining)]
		[AssertionMethod]
		public static void IsWellFormedRelativePath(
			string path,
			[InvokerParameterName] string argName)
		{
			Code.NotNullNorEmpty(path, argName);
			if (!PathHelper.IsWellFormedRelativePath(path))
				throw IoCodeExceptions.ArgumentRootedOrNotRelativePath(argName, path);
		}

		/// <summary>Asserts that specified path is well formed and ends with directory or volume separator chars.</summary>
		/// <param name="path">The path.</param>
		/// <param name="argName">Name of the argument.</param>
		[DebuggerHidden, MethodImpl(AggressiveInlining)]
		[AssertionMethod]
		public static void IsWellFormedContainerPath(
			string path,
			[InvokerParameterName] string argName)
		{
			Code.NotNullNorEmpty(path, argName);
			if (!PathHelper.IsWellFormedContainerPath(path))
				throw IoCodeExceptions.ArgumentNotVolumeOrDirectoryPath(argName, path);
		}

		/// <summary>Asserts that specified path is well-formed file name.</summary>
		/// <param name="path">The path.</param>
		/// <param name="argName">Name of the argument.</param>
		[DebuggerHidden, MethodImpl(AggressiveInlining)]
		[AssertionMethod]
		public static void IsFileName(
			string path,
			[InvokerParameterName] string argName)
		{
			Code.NotNullNorEmpty(path, argName);
			if (!PathHelper.IsFileName(path))
				throw IoCodeExceptions.ArgumentNotFileName(argName, path);
		}

		#region IO path
		/// <summary>Asserts that specified file does exist.</summary>
		/// <param name="filePath">Path to the file.</param>
		/// <param name="argName">Name of the argument.</param>
		[DebuggerHidden, MethodImpl(AggressiveInlining)]
		[AssertionMethod]
		public static void FileExists(
			string filePath,
			[InvokerParameterName] string argName)
		{
			Code.NotNull(filePath, argName);
			if (!File.Exists(filePath))
				throw Directory.Exists(filePath)
					? IoCodeExceptions.ArgumentDirectoryExistsFileExpected(argName, filePath)
					: IoCodeExceptions.ArgumentFileNotFound(argName, filePath);
		}

		/// <summary>Asserts that specified directory does exist.</summary>
		/// <param name="directoryPath">Path to the directory.</param>
		/// <param name="argName">Name of the argument.</param>
		[DebuggerHidden, MethodImpl(AggressiveInlining)]
		[AssertionMethod]
		public static void DirectoryExists(
			string directoryPath,
			[InvokerParameterName] string argName)
		{
			if (!Directory.Exists(directoryPath))
				throw File.Exists(directoryPath)
					? IoCodeExceptions.ArgumentFileExistsDirectoryExpected(argName, directoryPath)
					: IoCodeExceptions.ArgumentDirectoryNotFound(argName, directoryPath);
		}

		/// <summary>Asserts that specified path is not a path to existent file or a directory.</summary>
		/// <param name="path">The path.</param>
		[DebuggerHidden, MethodImpl(AggressiveInlining)]
		[AssertionMethod]
		public static void PathIsFree(string path)
		{
			if (Directory.Exists(path))
				throw IoCodeExceptions.DirectoryExists(path);
			if (File.Exists(path))
				throw IoCodeExceptions.FileExists(path);
		}
		#endregion
	}
}